#include "netchatsession.h"
#include "netservice.h"

#include <boost/asio.hpp>
#include <boost/bind.hpp>

NetChatSession::NetChatSession(NetChatRoom &a_room)
  : m_socket(NetService::IOService().getIOService())
  , m_room(a_room)
  , m_read_msg()
{
}

NetChatSession::TcpSocket& NetChatSession::socket()
{
  return m_socket;
}

void NetChatSession::start()
{
    m_room.join(shared_from_this());
    boost::asio::async_read(m_socket,
        boost::asio::buffer(m_read_msg.data(), NetChatMsg::header_length),
            boost::bind(
                &NetChatSession::handle_read_header, shared_from_this(),
                boost::asio::placeholders::error));
}

void NetChatSession::deliver(const NetChatMsg& a_msg)
{
    bool write_in_progress = !m_write_msgs.empty();
    m_write_msgs.push_back(a_msg);

    if (!write_in_progress)
    {
        boost::asio::async_write(m_socket,
            boost::asio::buffer(m_write_msgs.front().data(),
                m_write_msgs.front().length()),
                    boost::bind(&NetChatSession::handle_write, shared_from_this(),
                        boost::asio::placeholders::error));
    }
}

void NetChatSession::handle_read_header(const boost::system::error_code& a_error)
{
    if (!a_error && m_read_msg.decode_header())
    {
        boost::asio::async_read(m_socket,
            boost::asio::buffer(m_read_msg.body(), m_read_msg.body_length()),
                boost::bind(&NetChatSession::handle_read_body, shared_from_this(),
                    boost::asio::placeholders::error));
    }
    else
    {
        m_room.leave(shared_from_this());
    }
}

void NetChatSession::handle_read_body(const boost::system::error_code& a_error)
{
    if (!a_error)
    {
        m_room.deliver(m_read_msg);
        boost::asio::async_read(m_socket,
            boost::asio::buffer(m_read_msg.data(), NetChatMsg::header_length),
                boost::bind(&NetChatSession::handle_read_header, shared_from_this(),
                    boost::asio::placeholders::error));
    }
    else
    {
        m_room.leave(shared_from_this());
    }
}

void NetChatSession::handle_write(const boost::system::error_code& a_error)
{
    if (!a_error)
    {
        m_write_msgs.pop_front();
        if (!m_write_msgs.empty())
        {
            boost::asio::async_write(m_socket,
                boost::asio::buffer(m_write_msgs.front().data(),
                    m_write_msgs.front().length()),
                        boost::bind(&NetChatSession::handle_write, shared_from_this(),
                            boost::asio::placeholders::error));
        }
    }
    else
    {
        m_room.leave(shared_from_this());
    }
}
